<template>
  <div class="main-container1">
    <div class="main-card">
      <a href="/#/home">
        <span class="icon iconfont icon-shouye"></span>
      </a>
    <div class="card-header">
      <div class="header-title" style="position:relative;padding-left: 100px;"><img src="@/assets/images/top-logo.png" style="height:90px;position:absolute;top:-30px;left:20px;" alt="">一二三 · AI+政企服务助手|办事服务智能问答</div>
    </div>
    <div class="card-content">
      <div class="card-content-left">
    <el-drawer title="问答列表" :visible.sync="hisListDis" :show-close="false" size="20%" direction="ltr">
      <div class="history-list">
        <div v-for="(item, index) in historyList" :key="index" class="history-item"
          :class="{ active: currentSessionId === item.id }" @click="selectSession(item.id)">
          <div class="history-preview">{{ item.title }}</div>
        </div>
      </div>

    </el-drawer>

    <!-- 配置弹窗 -->
    <el-drawer :visible.sync="setDis" :show-close="false" size="20%" direction="rtl">
      <div style="padding:25px 20px;">
        <div>
          <span class="demonstration">知识库返回限制</span>
          <el-slider style="width:90%;margin:0 auto;" v-model="localLimit" :step="1" :min="1" :max="10"
            :marks="marksLimit" show-stops>
          </el-slider>
        </div>
        <el-divider />
        <div>
          <span class="demonstration">特色人声朗读</span>
          <el-row style="padding:18px 40px;" class="auto-audio-role rensheng">
            <el-button v-for="item in audioRole" :type="item.sex=='F'?'danger':'primary'" :plain="!item.checked" @click="audioRoleUp(item)"><img :src="item.url"/>{{ item.label }}</el-button>
          </el-row>
          

          <el-row style="padding:18px 40px;" class="auto-audio-role">
            <span class="demonstration" style="color:#72767b;">是否默认开启</span>
            <el-switch style="float:right;"
              v-model="isSetAudio">
            </el-switch>
          </el-row>
        
        </div>

        <!-- <div>
            <div style="padding-bottom: 10px;"><span class="demonstration">切换源</span></div>
            <el-radio-group v-model="fetchKey">
              <el-radio-button label="local" key="local">本地部署</el-radio-button>
              <el-radio-button label="deepseek" key="deepseek">云端对话</el-radio-button>
            </el-radio-group>
          </div> -->
      </div>
    </el-drawer>

    <!-- 右侧主界面 -->
    <div class="main-content">
      <div v-if="currentMessages.length < 2" style="position:absolute;top:35%;left:0;right:0;">
        <img src="@/assets/images/AIBg.png" alt=""
          style="width: 20%;margin:0 auto;display:block;width:150px;opacity: 0.08;">
        <div style="width:230px;margin:0 auto;color:#888;font-size: 15px;margin-top: 15px;text-align: center;">
          我可以帮你搜索、答疑，请把你的任务交给我吧~</div>
      </div>
      <!-- 消息容器 -->
      <div class="message-container" ref="messageContainer">
        <div v-for="(message, index) in currentMessages" :key="index" class="message-item" :class="message.role"
          v-if="message.role !== 'local'">
          <div class="message-avatar" v-if="message.role === 'assistant' || message.role === 'system'"><img :src="audioRoleKey.url" alt="AI"></div>
          <!-- <div class="message-avatar pc" v-else><img :src="avatar" alt="User"></div> -->

          <div class="message-bubble">
            <div v-if="message.role === 'assistant'"
              @click="message.thinkDis = !message.thinkDis" style="display: flex;margin-bottom: 10px;">
              <div v-if="message.generating" class="loading-spinner">
                <div class="spinner"></div>
              </div>
              <div style="line-height:30px;padding-left:15px;font-size: 15px;color: #727272;"> <span
                  v-if="message.generating">思考中... </span><span v-if="!message.generating">思考完成</span></div>
            </div>

            <div class="message-text">
              <div v-if="message.role === 'assistant' && message.thinkDis" class="think"
                v-html="upContent(message.think)"></div>
            </div>
            <div class="message-text" v-html="upContent(message.content)"></div>
            
            <!-- 音频按钮 -->
            <el-button v-if="message.role === 'assistant' && !message.generating" style="width:30px;margin:5px 0;"
              class="play-button"
              :type="isAudioStart.id==index&&isAudioStart.is ? 'danger' : 'success'"
              :icon="isAudioStart.id==index&&isAudioStart.is ? 'el-icon-video-pause' : 'el-icon-video-play'"
              circle
              size="mini"
              @click="isAudioStart.id==index&&isAudioStart.is ? audioStop(index) : audioStart(index)"
            />

            <!-- marked.parse(message.content) -->
             <div v-if="message.role === 'system'" class="">
              <el-divider></el-divider>
              <div class="rainbow-text" style="padding-bottom:15px;"><span class="icon iconfont icon-dengpao1" style="width:25px;color: #5151EE;"></span>&emsp;猜你想问</div>
              <div class="p-div p-dian" style="color:#1890ff">出生证明怎么办理？</div>
              <div class="p-div p-dian" style="color:#1890ff">社保怎么补办？</div>
             </div>
            <!-- <div v-if="message.role === 'assistant' || message.role === 'system'" class="message-time">{{ message.time
              }}</div> -->
            <!-- <div v-else class="message-time" style="color:#fff;">{{ message.time }}</div> -->
          </div>

        </div>
      </div>


      <!-- 输入区域 -->
      <div class="input-div">
        <div class="input-bg">
          <div v-if="inputStatus == 'yuyin'" style="position:relative;min-height: 34px;">
            <div v-if="!btnStopDis && inputMessage" style="padding-bottom:10px;color:#606266;">{{ inputMessage }}</div>
            <button @click="upInputStatus('dazi')" class="btnNone"><span class="iconBtn icon iconfont icon-icon-dazi" style="font-size: 25px;"/></button>
            <el-button v-if="!btnStartDis" @click="record" :disabled="btnStartDis" type="primary" size="mini" round
              plain style="position:absolute;left:calc(50% - 76px);width:150px;bottom:2px;">点击 说话</el-button>
            <el-button v-if="!btnStopDis" @click="stop" :disabled="btnStopDis" type="danger" size="mini" round plain
              style="position:absolute;left:calc(50% - 76px);width:150px;bottom:2px;">点击 停止</el-button>
          </div>
          
          <div v-else style="display:flex;min-height: 40px;">
            <button @click="upInputStatus('yuyin')" class="btnNone"><span class="iconBtn icon iconfont icon-yuyin1" style="font-size: 29px;"/></button>
            <el-input class="inputDivNone" :disabled="!isEnd" type="textarea" :autosize="{ minRows: 1, maxRows: 5 }"
              :clearable="true" :placeholder="isEnd?'输入你的问题':''" v-model="inputMessage" @keydown.enter.native="getLocalData"
              resize="none">
            </el-input>
            <el-button class="btnNone" v-if="!isEnd" size="medium" @click="cancelRequest()"><span class="iconBtn icon iconfont icon-16gf-stopCircle" style="font-size: 29px;"/></el-button>
            <el-button class="btnNone" v-if="isEnd" size="medium" @click="getLocalData()"><span class="iconBtn icon iconfont icon-fasonganniu" style="font-size: 29px;color:#0395F8;"/></el-button>
          </div>
        </div>

        <div style="padding:10px 5px 0;">
          <el-button style="padding:2px 10px;border:2px solid #9D9D9D;background:none;border-radius: 20px;float:left;"
            class="" size="medium" @click="createNewSession">
            <span class="iconBtn icon iconfont icon-a-lujing53834" style="font-size: 18px;color:#0395F8;float:left;width:auto;height:auto;"></span>
            <div style="line-height: 20px;font-size: 13px;padding-left:25px;color:#999;">新对话</div>
          </el-button>


          <div style="float:right;">
            <el-button class="" style="border:0;padding:0;background:none;float:left;margin-top: 2px;"
              @click="hisListDis = true">
              <span class="iconBtn icon iconfont icon-caidan1" style="font-size: 24px;"/>

            </el-button>
            <el-button style="border:0;padding:0;background:none;" size="medium" @click="setDis = true"><span class="iconBtn icon iconfont icon-shezhi" style="font-size: 29px;"/></el-button>
          </div>
        </div>

      </div>


    </div>
    <div class="stAudio">
      <audio id="myAudio" :controls="true"/>
    </div>

    <!-- <div ref="videoContainer" class="stAudio" style="top:60%;width: 300px;"></div> -->
  </div>
  <div class="card-content-right">
    <div>
      <div class="right-title">
        便民服务
      </div>
      <div class="right-content">
        <el-button v-for="item in bianminD" class="bianminBtn btnNone" type="primary" :style="'position:relative;background:#'+item.bg+' ;'"><span class="icon iconfont" :class="item.icon" style="color:#fff;position: absolute;top:1px;left:15px;"></span>{{ item.title }}</el-button>
      </div>
    </div>
    <div>
      <div class="right-title">
        特色创新服务
      </div>
      <div class="right-content">
        <div v-for="item in serveD" class="serveDiv">
          <div style="height:45px;text-align: center;">
            <span v-if="item.icon" class="icon iconfont" :class="item.icon" :style="'color:#'+item.color+';font-size:40px;display:block;'"></span>
            <img v-else :src="item.img" alt="" style="width:40px;height:40px;">
          </div>

          {{ item.title }}</div>

      </div>
    </div>
    <div>
      <div class="right-title">
        历史记录
      </div>
      <div class="right-content">
        <div class="p-div">新生儿办理出生证明</div>
        <div class="p-div">请假具体规定，用列表回复</div>
      </div>
    </div>
  </div>
  </div>
  </div>
</div>
</template>



<script>


import { mapGetters } from 'vuex'
import { chat, chatJava, chatLocal, chatStream } from "@/api/wenda/wenda";
import { getData } from "@/api/localserver/data";
import { listDeepseek, getDeepseek, addDeepseek, deleteDeepseek } from "@/api/wenda/deepseek";

import Recorder from 'recorder-core';
import WebSocketConnectMethod from '@/assets/js/wsconnecter';
import '@/assets/js/wav';
import '@/assets/js/pcm';

import tospeech from '@/api/wenda/tospeech.js'; // 引入 tospeech.js
import markdown from '@/utils/wenda/markdown.js'; // 引入定义样式后的markdown

import wss from "@/api/wenda/wss.js"; // 引入初始化方法


import aiNv from '@/assets/images/ai-role/nv.jpg';
import aiNan from '@/assets/images/ai-role/nan.jpg';
import kuaisuImg from '@/assets/images/icon-kuaisu.png';
import { init } from 'echarts';


export default {
  data() {
    return {
      bianminD:[
        {title:"社保中心",icon:"icon-shebaobutie",bg:"84CFF6",url:"",click:""},
      {title:"公积金",icon:"icon-a-ziyuan23",bg:"84CFF6",url:"",click:""},
      {title:"人社局",icon:"icon-zhengfu",bg:"7FA3F7",url:"",click:""},
      {title:"残联",icon:"icon-canlian",bg:"7FA3F7",url:"",click:""},
      {title:"住建局",icon:"icon-zhujianjulogo",bg:"7F86FB",url:"",click:""},
      {title:"民政",icon:"icon-minzhengtubiao1-48",bg:"7F86FB",url:"",click:""},
      ],
      serveD:[
      {title:"场景服务",icon:"icon-qitagongnengsvg",color:"FAAC6F",url:"",click:""},
      {title:"高效办成一件事",icon:null,img:kuaisuImg,color:"7F86FB",url:"",click:""},
      {title:"全生命周期",icon:"icon-xunhuan",color:"5AE8C2",url:"",click:""},
      {title:"一业一证",icon:"icon-yinzhangjieyong",color:"84CFF6",url:"",click:""},

      ],

      // texttospeech  
      audioRole:[
        {"voice":1088,checked:false, label:"女声01", "prompt": "开心",sex:"F",url:aiNv,"rm":"正常"},
        {"voice":102,checked:false, label:"女声02", "prompt": "开心",sex:"F",url:aiNv,"rm":"正常，太绵"},
        {"voice":1422,checked:false, label:"女声03", "prompt": "开心",sex:"F",url:aiNv,"rm":"机器味"},
        {"voice":7000,checked:false, label:"男声", "prompt": "开心",sex:"M",url:aiNan,"rm":"正常，呆"},
      ],
      audioRoleKey:{},

      audioSrc:[],
      toSpeechtest:/[？。！（]/,
      toSpeechKey:0,
      audioAutoKey:0,
      isSetAudio:true,
      isAutoAudio:false,
      audioOdj:null,
      isAudioStart:{id:0,is:false},

      // 语音
      inputStatus: "dazi",
      Uri: "/wssapi/",
      // Uri:"ws://192.168.97.10:10096/",
      btnStartDis: true,
      btnStopDis: true,
      wsconnecter: null,
      isRec: false,
      use_itn: true, // 逆文本标准化(ITN) true false
      asr_mode: "2pass",  // 选择asr模型模式  2pass  online  offline
      isfilemode: false, // 是否为文件模型
      sampleBuf: new Int16Array(),
      file: "", // 文件需要上传解析
      hotwords: {
        "政务服务": 50, 
        "一件事": 50,
        "营业执照": 50,
        "办理流程": 50,
        "员工手册": 50,
        "请假": 50,
        "旷工": 50
      },
      chunk_size: new Array(8, 16, 8),
      rec_text: "",
      offline_text: "",


      // 问答配置
      fetchKey: "local",
      fetchConfig: {
        deepseek: {
          url: "/deep/chat/completions",
          // url:"/deep/chat/completions",
          model: "deepseek-reasoner",
          apikey: 'Bearer sk-df0831cae1e446e3a1b20e32d6e597f9'
        },
        local: {
          // url:"http://macmini:11434/v1/chat/completions",
          // url:"/mac/v1/chat/completions",
          // model:"deepseek-r1:14b",
          // apikey:'ollama'
          url: "https://api.siliconflow.cn/v1/chat/completions",
          model: "deepseek-ai/DeepSeek-R1-Distill-Qwen-14B",
          apikey: 'Bearer sk-taalmohasunalwcsqnjgpnsfdxqeuofkhnbimbmzlcptdgld'
        }
      },
      marksLimit: {
        1: '1',
        2: '2',
        3: '3',
        4: '4',
        5: '5',
        6: '6',
        7: '7',
        8: '8',
        9: '9',
        10: '10',
      },
      localLimit: 3,


      // 问答模块
      selVal: {
        value: 'one',
        label: '一件事',
        msg: '您好，政务助手为您服务，可以直接提问或语音咨询，我帮您快速解答。'
      },
      selOp: [{
        value: 'one',
        label: '一件事',
        msg: '您好，政务助手为您服务，可以直接提问或语音咨询，我帮您快速解答。'
      }, {
        value: 'two',
        label: '员工手册',
        msg: '您好，员工手册助手为您服务，可以直接提问或语音咨询，我帮您快速解答。'
      }],


      sidebarCollapsed: true, // 左侧列表伸缩-true收缩
      hisListDis: false,  // 左侧列表显示状态-false隐藏
      setDis: false,
      inputMessage: '', // 输入框内容
      historyList: [{
        id: 1,
        title: '新问题',
        preview: '...',
        messages: []
      }],
      currentSessionId: 1,
      // 打字效果
      isGenerating: false, // 打字状态
      displayContent: "",
      typingSpeed: 30, // 打字速度（毫秒/字）
      maxBubbleWidth: '70%',

      minBubbleHeight: '60px',
      // 流式消息
      isEnd: true,
      isUserEnd: false, // 用户终止的标志
      decoder: null,
      controller: null, // 存储 AbortController 实例
      isThink: false,
      isContent: false,
    }

  },
  computed: {
    // ...mapGetters([
    //   'avatar',
    // ]),
    sidebarWidth() {
      return this.sidebarCollapsed ? '110px' : '15%'
    },
    currentMessages() {
      const session = this.historyList.find(
        item => item.id === this.currentSessionId
      )
      return session ? session.messages : []
    }
  },
  created() {
    this.init()
  },
  mounted() {

  },
  watch: {
    isSetAudio(newVal, oldVal) {
      localStorage.setItem('wdSet-isSetAudio', newVal)

    },
    localLimit(newVal, oldVal) {
      localStorage.setItem('wdSet-localLimit', newVal)

    },
    audioRoleKey(newVal, oldVal) {
      localStorage.setItem('wdSet-audioRoleKey', JSON.stringify(newVal))
    }
  },
  methods: {
    init() {
      // 获取本地存储的值
      localStorage.getItem('wdSet-localLimit') !== null && (this.localLimit = +localStorage.getItem('wdSet-localLimit'))
      localStorage.getItem('wdSet-isSetAudio') !== null && (this.isSetAudio = localStorage.getItem('wdSet-isSetAudio'))

      // 从 localStorage 获取音频角色配置
      const rolekey = localStorage.getItem('wdSet-audioRoleKey')
      const audioRoleData = rolekey ? JSON.parse(rolekey) : this.audioRole[0]
      this.audioRoleUp(audioRoleData)

      // 初始化 WebSocket 连接
      this.wsconnecter = wss.initWebSocket({
        file: this.file,
        stateHandle: this.getConnState,
        msgHandle: this.getJsonMessage,
        use_itn: this.use_itn,
        asr_mode: this.asr_mode,
        isfilemode: this.isfilemode,
        hotwords: this.hotwords
      });

      // 定义一个utf-8格式的文本解码器
      this.decoder = new TextDecoder('utf-8')
      // this.getHistoryList()
      let key = this.$route.query.key
      console.log(this.$route.query)
      if (this.$route.query.key) {
        this.selOp.map((item, i) => {
          if (item.value == key) {
            this.selVal = item
            this.createNewSession()
          }
        })
      }
      // 开屏交互后播放语音
      this.fetchToSpeech({
        text:"",
        to_i:-1,
        msg_i:0,
        voice:this.audioRoleKey.voice,
        prompt:this.audioRoleKey.prompt,
        msg_content:this.selVal.msg,
        all:true,
        toSpeechtest:this.toSpeechtest,
        form:"audioStart",
      })
    },
    async fetchToSpeech(data) {
        
          let res = await tospeech.sendPostRequestApi(data);
          if(res.audioInit){
            this.audioStop(res.msg_i)
            this.audioInit(res.msg_i)
          }
          this.audioSrc[res.to_i] = res.audioSrc;
          // 检查是否需要启动音频播放，且确保只调用一次播放音频
          if (res.audioStart && !this.audioAutoKey && !this.isAutoAudio) { 
            // 如果 res.audioStart 为 true，并且当前没有自动播放音频的状态，则执行以下操作
            // 更新 isAudioStart 状态，标记当前消息的音频播放状态
            this.isAudioStart = res.isAudioStart;
            // 将返回的音频源数据存储到 audioSrc 数组中，索引为 res.to_i
            console.log("朗读前打印 sendPostRequest001 ：", this.audioSrc);
            // 调用 audioStart 方法，启动指定消息索引的音频播放
            this.audioStart(res.msg_i);
          }
          if(this.currentMessages[res.msg_i] && res.all){
            this.toSpeechKey++
            // console.log("重新调用：",'',this.toSpeechKey,msg_i,true)
            res.audioInit = false
            res.audioSrc = {}
            res.audioStart = false
            res.to_i++
            console.log("重新调用",res)
            this.fetchToSpeech(res)
          }
          console.log(this.audioSrc)

       
    },
    audioStart(msg_i){ // 自动播放音频
      (msg_i!=this.isAudioStart.id)
      console.log("audioStart --问答下标：",msg_i,"当前数据：",this.isAudioStart,"--语音数据：",this.audioSrc,"--播放下标：",this.audioAutoKey)
      if(msg_i!=this.isAudioStart.id && this.isEnd){ // 数据不匹配，没有正在回答的问题
        console.log("audioStart,重新加载语音数据")
        // this.sendPostRequest('',-1,msg_i,true,"audioStart")
        this.fetchToSpeech({
          text:"",
          to_i:-1,
          msg_i:msg_i,
          voice:this.audioRoleKey.voice,
          prompt:this.audioRoleKey.prompt,
          msg_content:this.currentMessages[msg_i].content,
          all:true,
          toSpeechtest:this.toSpeechtest,
          form:"audioStart",
        })
        return false
      }
      // 自动播放音频
      if(!this.audioSrc[this.audioAutoKey].src || !this.audioSrc[this.audioAutoKey]){ // 当前播放音频为空，重新播放，直至音频存在
        console.log("audioStart==this.audioSrc[this.audioAutoKey]==",this.audioSrc[this.audioAutoKey])
        setTimeout(() => {
          this.audioStart(msg_i)
        }, 1500);
        return false
      }
      this.audioOdj = new Audio(this.audioSrc[this.audioAutoKey].src);
      this.audioOdj.addEventListener('ended', () => {
        if(msg_i){ // 第一条消息，msg_i==0时不继续朗读
          setTimeout(() => {
            this.audioAppend(msg_i)
          }, 500);
        }
      });
      this.audioOdj.controls = true
      this.audioOdj.id = "myAudio";
      this.audioOdj.className = "stAudio";
      document.getElementById('myAudio').replaceWith(this.audioOdj)

      this.audioOdj.play().then(() => {
        this.isAutoAudio = true
        // console.log("play",botMessage)
        this.isAudioStart = {id:msg_i,is:true}

        console.warn("语音开始播放 ******* 当前KEY",this.audioAutoKey,"总长：",this.audioSrc.length,this.audioSrc[this.audioAutoKey].text)
      }).catch(error => {
          console.error("播放失败：问答下标：", msg_i,"--当前播放KEY：",this.audioAutoKey,error);
          console.log("语音数据：", JSON.stringify(this.audioSrc[this.audioAutoKey]));
          this.isAudioStart = {id:msg_i,is:false}
          setTimeout(() => {
            this.audioStart(msg_i)
          }, 2000);
      });
    },
    audioAppend(msg_i){ // 播放音频追加调用


      if(this.audioSrc[this.audioAutoKey] && this.audioSrc[this.audioAutoKey].src){ // 确认信息后删除
        URL.revokeObjectURL(this.audioSrc[this.audioAutoKey].src); // 释放已经播放完成的音频
      }
      let msgArr = this.currentMessages[msg_i].content.split(this.toSpeechtest);
      console.log("audioAppend-追加播放时对比长度：msg_i："+msg_i+"，audioAutoKey："+this.audioAutoKey+"，audioSrc.length："+this.audioSrc.length+"，msgArr.length："+msgArr.length )
      // msgArr
      // 当前播放下标超过实际文字截取后的长度，才会停止
      if(this.audioAutoKey>=this.audioSrc.length-1){ // 比对数据长度
        setTimeout(() => {
          if(this.audioAutoKey<this.audioSrc.length-1 ){ // 无数据后延时3秒继续判断
            this.audioAutoKey++ //播放音频
            this.audioStart(msg_i)
          }else{
            console.warn("播放完成")
            this.isAudioStart = {id:msg_i,is:false}
            // console.log("&&&&&&&audioAppend修改isAudioStart",this.isAudioStart)
            this.audioInit(msg_i)
          }
        }, 500);
      }else{
        this.audioAutoKey++ //播放音频
        this.audioStart(msg_i)
      }
    },
    audioStop(msg_i) {
      this.isAudioStart = {id:msg_i,is:false}
      if (this.audioOdj) {
        this.audioOdj.pause();
        this.audioOdj = null;
      }
    },
    audioInit(msg_i){
      console.log("audioInit",msg_i)
      this.audioOdj = null
      this.toSpeechKey = 0
      this.audioAutoKey = 0
      this.isAutoAudio = false
      this.audioSrc = [] // blob数据已经逐个释放
      this.isAudioStart = {id:0,is:false}
    },
    audioRoleUp(data){
      this.audioRoleKey = data
      for (let i = 0; i < this.audioRole.length; i++) {
        const voice = this.audioRole[i].voice;
        if (voice == this.audioRoleKey.voice) {
          console.log(voice)
          this.audioRole[i].checked = true
        }else{
          this.audioRole[i].checked = false
        }
      }
    },
    // 录音开始
    upInputStatus(st) {
      if(this.isRec){
        this.$modal.msgWarning("录音中，终止后使用语音功能");
        return false
      }
      if(!this.isEnd){
        this.$modal.msgWarning("回答中，终止后可使用语音功能");
        return false
      }
      this.inputStatus = st
      if (st == 'yuyin') {
        this.start()
      }
    },

    async record() { // (2)(点击录音/获取权限)点击按钮，开始调起录音
      const recorder = new Recorder({
        type: "pcm",
        bitRate: 16,
        sampleRate: 16000,
        numChannels: 1,  // 

        audioTrackSet: { // 新增音频轨道配置
          echoCancellation: true,    // 启用回声消除
          noiseSuppression: true,    // 启用噪声抑制
          autoGainControl: false,      // 禁用自动增益控制
          channelCount: 1 // 单声道
        },
        onProcess: this.recProcess,
      });
      recorder.open(() => {
        this.isRec = true
        this.btnStartDis = true;
        this.btnStopDis = false;
        console.log("record()(2)(点击录音/获取权限)开始录音")
        recorder.start();

      }, (error) => { // 新增错误回调参数
        // this.$modal.msgWarning(error);
        this.inputStatus = "dazi"
        this.$modal.msgError(error);
      });
      this.rec = recorder;

    },
    start() { // (1)(链接)识别启动、停止、清空操作
      this.clear();
      // 启动连接
      if (this.wsconnecter && typeof this.wsconnecter.wsStart === 'function') {
        try {
          var ret = this.wsconnecter.wsStart(this.Uri);
          // 1 is ok, 0 is error
          if (ret == 1) {
            console.log("进入start()(1)(链接)正常开始")
            // this.$modal.msgWarning("正在连接asr服务器，请稍后...")
            // this.inputStatus="dazi"
            this.btnStartDis = false;
            this.btnStopDis = true;
            console.log(401, this.btnStopDis)

            return 1;
          } else {
            console.log("进入start()出错，无法开始，连接失败")
            this.$modal.msgError("无法连接asr服务器，请重试")
            this.inputStatus="dazi"
            this.btnStartDis = true;
            this.btnStopDis = true;
            console.log(411, this.btnStopDis)
            return 0;
          }
        } catch (error) {
          this.inputStatus = "dazi"
          this.btnStartDis = true;
          this.btnStopDis = true;
          console.log(418, this.btnStopDis)
          this.$modal.msgError(error)
          console.error('wsStart 调用失败: WebSocket 连接启动失败，请重试:', error);
        }
      } else {
        this.inputStatus = "dazi"
        this.btnStartDis = true;
        this.btnStopDis = true;
        console.log(428, this.btnStopDis)
        this.$modal.msgError("未正确初始化")
        console.error('wsconnecter 未正确初始化或 wsStart 方法不存在:WebSocket 连接器未正确初始化，请重试');
      }
    },
    stop() { // 停止连接
      if (this.wsconnecter && typeof this.wsconnecter.wsStop === 'function') {

        try {
          // 控件状态更新

          var request = {
            "chunk_size": this.chunk_size,
            "wav_name": "h5",
            "is_speaking": false,
            "chunk_interval": 10,
            "mode": this.asr_mode,
          };
          console.log(request);
          if (this.sampleBuf.length > 0) {
            this.wsconnecter.wsSend(this.sampleBuf);
            console.log("sampleBuf.length" + this.sampleBuf.length);
            this.sampleBuf = new Int16Array();
          }
          this.wsconnecter.wsSend(JSON.stringify(request));

          console.log("发送完数据,请等候,正在识别...");
          this.isRec = false;


          if (!this.isfilemode) {
            this.inputStatus = 'dazi' // 停止录音，切回打字状态
            this.btnStopDis = true;
            console.log(442, this.btnStopDis)
            this.btnStartDis = true;
            // 等待 3s 后断开 WebSocket
            setTimeout(() => {
              console.log("call stop ws!");
              this.wsconnecter.wsStop();
              console.log("延时三秒断开，请点击连接");
            }, 3000);

            // 停止录音
            if (this.rec && typeof this.rec.stop === 'function') {
              this.rec.stop((blob, duration) => {
                console.log("停止录音,打印录音文件", blob);
                var audioBlob = Recorder.pcm2wav({
                  sampleRate: 16000,
                  bitRate: 16,
                  blob: blob
                }, (theblob, duration) => {
                  console.log(theblob);
                  let src = (window.URL || webkitURL).createObjectURL(theblob);
                  console.log(src)
                }, (msg) => {
                  console.log(msg);
                });
              }, (errMsg) => {
                console.log("errMsg: " + errMsg);
              });
            }
          }
        } catch (error) {
          console.error('WebSocket 连接停止失败，请重试  wsStop 调用失败:', error);
        }
      } else {
        console.error('wsconnecter 未正确初始化或 wsStop 方法不存在');
      }
    },
    getConnState(connState) { // 连接状态响应
      console.log("connState", connState);
      if (connState === 0) { // on open
        console.log('getConnState  连接成功!请点击开始');
        this.inputStatus = "yuyin"
        this.btnStartDis = false;
        this.btnStopDis = true;
      } else if (connState === 1) {
      } else if (connState === 2) {
        this.stop();
        console.log("连接地址" + this.Uri + "失败,请检查asr地址和端口。或试试界面上手动授权，再连接。");
        this.btnStartDis = true;
        this.btnStopDis = true;
        // console.log(499,this.btnStopDis)
      }
    },
    clear() { // 重置
      // var varArea=document.getElementById('varArea');
      this.inputMessage = "";
      this.rec_text = "";
      this.offline_text = "";
    },
    getJsonMessage(jsonMsg) { // 获取识别结果
      console.log("###################getJsonMessage: " + JSON.parse(jsonMsg.data)['text']);
      var rectxt = "" + JSON.parse(jsonMsg.data)['text'];
      var asrmodel = JSON.parse(jsonMsg.data)['mode'];
      var timestamp = ''; // 返回语音录制的秒数，目前在ws下有值，wss未定义
      if (asrmodel == "2pass-offline" || asrmodel == "offline") {
        this.offline_text = this.offline_text + wss.handleWithTimestamp(rectxt, timestamp); // rectxt; //.replace(/ +/g,"");
        this.rec_text = this.offline_text;
      } else {
        this.rec_text = this.rec_text + rectxt; //.replace(/ +/g,"");
      }
      this.inputMessage = this.rec_text;
      console.log("rec_text: " + this.rec_text);
    },
    recProcess(buffer, powerLevel, bufferDuration, bufferSampleRate, newBufferIdx, asyncEnd) { // 实时处理录音数据                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
      // this.sampleBuf = new Int16Array()
      let sendBuf
      if (this.isRec === true) {
        var data_48k = buffer[buffer.length - 1];
        var array_48k = new Array(data_48k);
        var data_16k = Recorder.SampleData(array_48k, bufferSampleRate, 16000).data;
        this.sampleBuf = Int16Array.from([...this.sampleBuf, ...data_16k]);
        var chunk_size = 960; // for asr chunk_size [5, 10, 5]
        while (this.sampleBuf.length >= chunk_size) {
          sendBuf = this.sampleBuf.slice(0, chunk_size);
          this.sampleBuf = this.sampleBuf.slice(chunk_size, this.sampleBuf.length);
          try {
            this.wsconnecter.wsSend(sendBuf);
          } catch (error) {
            this.stop();
          }
        }
      }
    },
    // 录音结束



    // 格式化返回内容
    upContent(text) {
      if (text) {
        return markdown.returnContent(text)
      }
    },
    // 选择问答模块切换
    upSelVal(i) {
      this.selVal = this.selOp[i]
    },
    // 后台获取列表
    getHistoryList() {
      listDeepseek().then(res => {
        res.map((data, i) => {
          this.historyList.push({ id: data.id, title: "相关问题", preview: data.question, messages: [] })
        });
      })
    },
    // 保存问答数据
    saveWenda() {
      console.warn(this.currentMessages)
      let L = this.currentMessages.length
      console.log(L)
      if (L < 3) {
        console.error("保存问答数据==>>记录不足三条，取消保存")
        return false
      }
      let addData = {
        arentId: 0, name: "",
        questionTime: "", question: "",
        answer: "", answerTime: ""
      }
      for (let i = (L - 3); i < L; i++) {

        let r = this.currentMessages[i]
        if (r.role == "user") {
          addData.name = r.content
        }
        if (r.role == "local") {
          addData.question = r.question
          addData.questionTime = r.questionTime
        }
        if (r.role == "assistant") {
          addData.answer = r.answer
          addData.answerTime = r.answerTime
        }
      }

      // 父级id parentId int类型 首次为0
      // 用户提问内容 name string类型
      // 用户提问时间 questionTime 日期类型
      // 本地知识库内容 question string类型
      // deepseek回答内容 answer string类型
      // 回答时间 answerTime 日期类型
      // addDeepseek(addData).then(res=>{
      //   console.log("add返回",res)
      // })
    },
    // 新问题
    createNewSession() {
      // 新问题首先发消息
      let msgD = []
      if (this.selVal.value) {
        msgD = [{
          role: 'system',
          think: '',
          thinkDis: false,
          content: this.selVal.msg,
          generating: false,
          time: new Date().toLocaleDateString() + " " + new Date().toLocaleTimeString()
        }]
      }
      // this.currentMessages = []
      const newSession = {
        id: Date.now(),
        title: '',
        preview: '',
        messages: msgD
      }
      this.historyList.unshift(newSession)
      this.currentSessionId = newSession.id
    },
    // 填充当前问题的历史内容currentSession
    selectSession(sessionId) {
      getDeepseek(sessionId).then(res => {
        this.currentSessionId = sessionId
        const currentSession = this.historyList.find(
          item => item.id === this.currentSessionId
        );
        currentSession.messages = res;
        // that.currentMessages = res
        // console.log("res",res)
        // console.log("selectSession",currentSession)
        this.historyList.map((data, i) => {
          if (data.id == sessionId) {
            data.message = res
          }
        });
      })


    },
    // 请求本地服务中的数据+整理最终请求数据
    getLocalData() {
      if(!this.inputMessage.length){
        this.$modal.msgWarning("请输入有效的提问信息");
        return false
      }

      // 语音预回答
      this.fetchToSpeech({
        text:"",
        to_i:-1,
        msg_i:0,
        voice:this.audioRoleKey.voice,
        prompt:this.audioRoleKey.prompt,
        msg_content:"好的，我来为您解答疑问。",
        all:true,
        toSpeechtest:0,
        form:"getLocalData",
      })

      getData(this.selVal.value, this.inputMessage).then(res => {
        console.log("localLimit:", this.localLimit)
        let data = []
        for (let i = 0; i < this.localLimit; i++) {
          data.push(res.documents[i])
        }
        let str = "你是用于回答问题的助手。请使用以下检索到的上下文来回答问题。如果你不知道答案，就直接说你不知道，可以使用表情增强语气，请使用简体中文回答。问题： {" + res.prompt + "} 上下文：{" + JSON.stringify(data) + "} 回答:"
        // 用户消息
        const userMessage = {
          role: 'user',
          content: this.inputMessage, // 去除用户点击回车后的换行符
          time: new Date().toLocaleDateString() + " " + new Date().toLocaleTimeString(),
        };
        // 本地消息
        const localMessage = {
          role: 'local',
          content: str,
          time: new Date().toLocaleDateString() + " " + new Date().toLocaleTimeString()
        };
        // AI消息（带加载状态）
        const botMessage = {
          role: 'assistant',
          think: '',
          thinkDis: false,
          content: '',
          generating: true,
          time: new Date().toLocaleDateString() + " " + new Date().toLocaleTimeString()
        };

        // 添加到消息列表
        const currentSession = this.historyList.find(
          item => item.id === this.currentSessionId
        );

        currentSession.messages.push(userMessage);
        currentSession.messages.push(localMessage);
        currentSession.messages.push(botMessage);

        console.log("添加三种数据到msg", currentSession.messages)
        this.chatUserCLickStream(str, botMessage)
      })
    },

    // stream:true 开始
    // 异步流式--提交
    async chatUserCLickStream(realQues = "", botMessage) {

      // 正在执行？
      if (!this.isEnd) {
        return false
      }

      let aa = this.currentMessages
      console.log("原记录", this.currentMessages)
      console.log("记录长度", this.currentMessages.length)
      // 修改数据结构
      let BCmsgArr = []
      for (let i = 0; i < aa.length - 2; i++) {
        if (aa[i].role != 'user') {
          BCmsgArr.push({ role: (aa[i].role == 'local') ? 'user' : aa[i].role, content: aa[i].content })
        }
      }
      console.log("之前的历史记录", BCmsgArr)

      //  ?????
      if (!this.inputMessage.trim() || this.isGenerating) return;

      console.log('用户点击了聊天', this.inputMessage)
      if (realQues) {
        BCmsgArr.push({ role: "user", content: realQues })
      } else {
        BCmsgArr.push({ role: "user", content: this.inputMessage })
      }

      console.log("提交预览", BCmsgArr)
      //  重置输入框
      this.inputMessage = ""
      this.isEnd = false; // 重置状态
      this.isUserEnd = false;
      this.isThink = false
      this.isContent = true

      // 增加AbortController 可中断请求
      if (this.controller) {
        this.controller.abort();
      }

      // 创建新的 AbortController
      this.controller = new AbortController();
      try {
        const data = {
          messages: BCmsgArr,
          model: this.fetchConfig[this.fetchKey].model,
          stream: true,
          "options": {
            "num_ctx": 128000
          }
        }

        console.log("fetch.url==>>", this.fetchConfig[this.fetchKey].url)
        const res = await fetch(this.fetchConfig[this.fetchKey].url, {
          method: 'post',
          body: JSON.stringify(data),
          responseType: 'srteam',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': this.fetchConfig[this.fetchKey].apikey
          },
          signal: this.controller.signal
        }).catch(error => {
            console.error('Fetch error:', error);
        });

        if (!res.ok) throw new Error('请求失败');

        // const res = await chatStream(BCmsgArr)  // 使用js中的方法请求
        // console.log("返回数据：",JSON.stringify(res.body))
        const reader = res.body.getReader()
        console.log("开始")
        // 响应之后重置语音状态
        this.audioInit(0)
        this.audioStop(0)
        // 响应之后重置时间
        botMessage.time = new Date().toLocaleDateString() + " " + new Date().toLocaleTimeString()

        while (!this.isEnd) {
          await this.getText(reader, botMessage,this.currentMessages.length-1) // 处理响应数据
        }

      } catch (err) {
        console.log("异步代码出错-chatUserCLickStream")
        // 如果是主动取消的请求，不视为错误
        if (err.name !== 'AbortError') {
          this.error = err.message || '请求出错';
        }
      } finally {
        // console.log("用户中断数据返回")
        this.isEnd = true;
        this.controller = null; // 请求结束后清空 controller
      }
    },
    // 异步提交后，处理返回数据
    async getText(reader, botMessage, msgI) {
      return new Promise(async (resolve, reject) => {
        // 读取流中的下一个数据块
        const { value, done: readerDone } = await reader.read()

        // 用户可能会中断问答
        if (this.isEnd || this.isUserEnd) {
          console.error("getText-回答中断[进程，用户]", this.isEnd, this.isUserEnd)
          return false
        }

        this.isEnd = readerDone

        // 将数据块解码为字符串
        let chunk = this.decoder.decode(value, { stream: true });
        if (chunk) {
          const events = chunk.trim().split('\n\n')

          events.forEach((event) => {
            const lines = event.split('\n')
            lines.forEach((line) => {
              if (line.startsWith('data: ')) {
                // console.log(new Date().toLocaleTimeString(),line)
                try {
                  // 更新消息内容
                  let isToSpeech = false
                  const data = JSON.parse(line.slice(6))
                  if (data.choices[0].delta.reasoning_content) {
                    this.isThink = true
                    this.isContent = false
                    botMessage.think += data.choices[0].delta.reasoning_content ? data.choices[0].delta.reasoning_content : ''
                    // console.log("写入Think") // ,data.choices[0].delta.reasoning_content
                    
                  } else {
                    botMessage.content += data.choices[0].delta.content ? data.choices[0].delta.content : ''
                    // if(data.choices[0].delta.content.indexOf("。") !== -1){
                    if(this.isSetAudio && this.toSpeechtest.test(data.choices[0].delta.content)){
                      isToSpeech = true
                    }
                  }
                  // 调整生成状态(只会进入一次)
                  if (botMessage.generating && data.choices[0].delta.content && data.choices[0].delta.content.length) {
                    this.isThink = false
                    this.isContent = true
                    isToSpeech = true
                    // console.log("关于关于关于关于",this.currentMessages[msgI-2].content)
                    botMessage.content = "关于["+this.currentMessages[msgI-2].content.replace(/\n/g, '')+"]的问题，您需要了解以下信息。 "+botMessage.content
                    botMessage.generating = false
                  }
                  // 有内容之后调用ttsapi
                  if (isToSpeech && botMessage.content.length > 0) {
                    let msgArr = botMessage.content.split(this.toSpeechtest);
                    // console.log("getData打印，文本截取后数据长度",msgArr.length)
                    // console.log("%%%%%%%%%%要写入的数据内容和对应下标",msgArr[this.toSpeechKey],this.toSpeechKey)
                    
                    // 携带截取后的数据下标，保证异步写入顺序正常
                    console.log(this.toSpeechKey,msgArr)
                    this.fetchToSpeech({
                      text:msgArr[this.toSpeechKey],
                      to_i:this.toSpeechKey,
                      msg_i:msgI,
                      msg_content:"",
                      all:false,
                      voice:this.audioRoleKey.voice,
                      prompt:this.audioRoleKey.prompt,
                      form:"生成",
                    })
                    // this.sendPostRequest(msgArr[this.toSpeechKey],this.toSpeechKey,msgI,false,"生成.1") // 携带截取后的数据下标，保证异步写入顺序正常
                    this.toSpeechKey++
                  
                      
                  }

                } catch (e) {
                  // console.warn("catch",line.replace(/^data: \s*/, ""))
                  // alert(e)
                  if (line.replace(/^data: \s*/, "") == "[DONE]") {
                    console.warn("对话结束，发起同步")
                    // alert(botMessage)
                    // alert(botMessage.content)
                    this.isEnd = true
                    this.saveWenda()
                  }
                }
              }
            })
          })
          // console.log("是否结束",this.isEnd)
          resolve()
        } else resolve()
      })
    },
    // stream:true 结束

    // 中断回答操作，重置数据
    cancelRequest() {
      if (this.controller) {
        this.controller.abort();
        this.controller = null;
        this.isEnd = true;
        this.isUserEnd = true;
        // 重置语音状态
        this.audioStop(0)
        this.audioInit(0)
        console.log(this.currentMessages)
        // 消息列表
        const upMsg = this.historyList.find(
          item => item.id === this.currentSessionId
        );
        // console.log("cancelRequest--消息列表",upMsg)
        // console.log("cancelRequest--消息列表",upMsg.messages.length)
        upMsg.messages[upMsg.messages.length - 1].content = "您中断了回答。可以继续提问..."
        upMsg.messages[upMsg.messages.length - 1].generating = false
        console.error("cancelRequest--回答中断[进程，用户]", this.isEnd, this.isUserEnd)
      }
    },

    // 动态对话框
    scrollToBottom() {
      this.$nextTick(() => {
        const container = this.$refs.messageContainer
        container.scrollTop = container.scrollHeight
      })
    },

  },
  // 组件销毁时自动取消请求（防止内存泄漏）
  beforeUnmount() {
    if (this.controller) {
      this.controller.abort();
    }
  },
}
</script>

<style scoped>
.main-container1 {
  display: flex;
  /* height: calc(100vh - 85px); */
  height: calc(100vh);
  padding:55px;
  background: -webkit-linear-gradient(#C2DBFA,#DAE0FA,#F5F6FA);
  background: linear-gradient( #C2DBFA,#DAE0FA, #F5F6FA);
}
.main-card{
  box-sizing: border-box;
  width: 100%;
  background: #fff;
  border-radius: 15px;
  box-shadow: rgba(100, 100, 100, 0.1) 1px 2px 3px;
  position:relative;
}
.card-header{
  border-radius: 15px 15px 0 0;
  height:100px;
  background: -webkit-linear-gradient(to right,#409eff,#B271B7, #409eff,#2E82FC,#1890FF,#409eff,#1890FF,#409eff);
  background: linear-gradient(to right,#409eff,#B271B7, #409eff,#2E82FC,#1890FF,#409eff,#1890FF,#409eff);
}

.header-img{
  height:100%;
}
.header-img img{
  height:120%;
}
.header-title{
  height: 100%;
  line-height: 80px;
  padding-left:30px;
  color:#fff;
  font-size: 23px;
  letter-spacing: 8px;
}
.header-title>div{
  height:100%;overflow: hidden;
}
.card-content{
  margin-top:-20px;
  background: #fff;
  height:calc(100% - 80px);
  display: flex;
  overflow: hidden;
  border-radius: 15px;
}
.card-content-left{
  width:calc(100% - 360px);
  position:relative;
  height:100%;
  background: #F6F7FB;
}




.card-content-right{
  background: #fff;
  width:360px;
  height:100%;
  padding-left:20px;
}
.right-title{
  position: relative;padding-left:12px;line-height: 50px;font-size: 18px;color:rgba(0, 0, 0, 0.8);
}
.right-title::before{
  content:" ";
  position:absolute;top:16px;left:0;
  height:18px;width:3px;border-radius: 3px; background: #246BD4;
}
.right-content{
  padding-bottom:40px;
}
.bianminBtn{
  width:45%;
  background: #83D1F7;
  height:45px;
  line-height: 45px;
  margin:10px 5% 0 0;
}

.serveDiv{
  width:45%;
  display: inline-block;
  box-sizing: border-box;
  line-height: 35px;
  margin:10px 5% 0 0;
  text-align: center;
}

.rainbow-text {
  color: #000; /* 设置基础文字颜色 */
  background: -webkit-linear-gradient(to right, #1890FF,#181cff,#B271B7);
  background: linear-gradient(to right, #1890FF,#181cff, #B271B7);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}
.p-div{
  line-height: 35px;
  font-size: 15px;
  color:rgba(0, 0, 0, 0.8);
}
.p-dian{position:relative;padding-left:20px;}
.p-dian::before{
  content:" ";
  position:absolute;top:15px;left:10px;
  height:3px;width:3px;border-radius: 3px; background: #246BD4;
}



























.history-sidebar {
  width: 15%;
  background: #ffffff;
  box-shadow: 2px 0 6px rgba(0, 0, 0, 0.1);
  transition: width 0.3s ease;
  overflow: hidden;
}

.sidebar-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px;
  border-bottom: 1px solid #ebeef5;
}

.history-list {
  height: calc(100vh - 75px);
  overflow-y: auto;
}

.history-item {
  padding: 12px;
  border-bottom: 1px solid #f0f2f5;
  cursor: pointer;
}

.history-item:hover {
  background: #f5f7fa;
}

.history-item.active {
  background: #e8f4ff;
}

.history-title {
  font-weight: 500;
  color: #303133;
  margin-bottom: 4px;
}

.history-preview {
  font-size: 12px;
  color: #909399;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.main-content {
  flex: 1;
  display: flex;
  height: 100%;
  flex-direction: column;
}

.message-container {
  flex: 1;
  overflow-y: auto;
  padding: 20px 0 180px;
}

.message-item {
  display: flex;
  margin-bottom: 20px;
}

.message-item.user {
  flex-direction: row-reverse;
}

.message-avatar {
  width: 40px;
  height: 40px;
  margin: 0 12px;
}

.message-avatar img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
}

.message-bubble {
  max-width: 80%;
  overflow: hidden;
  padding: 10px 16px;
  border-radius: 20px;
  position: relative;
  box-shadow: rgba(100, 100, 100, 0.1) 2px 3px 5px;
}

.message-item.user .message-bubble {
  background: rgb(110, 177, 248);
  color: white;
  padding: 3px 16px 2px;
  margin-right: 10px;
}

.message-item.assistant .message-bubble,.message-item.system .message-bubble  {
  background: white;
  border: 1px solid #ebeef5;
}

.message-text {
  line-height: 1.6;
}

.message-time {
  font-size: 12px;
  color: #909399;
  margin-top: 6px;
}

.input-div {
  background: #F6F7FB;
  padding: 15px 20%;
  box-shadow: 0 0 10px 5px #F6F7FB;
  position: absolute;
  bottom: 0;
  right: 0;
  left: 0;
}

.input-bg {
  background: #fff;
  border-radius: 20px;
  padding: 10px 15px;
  box-shadow: rgba(100, 100, 100, 0.1) 1px 2px 3px;
  position: relative;

}

.input-div {
  /* margin:0 150px 15px;
  display: flex; */
}

.input-area {
  position: relative;
  left: 25px;
  bottom: 5px;
  width: calc(100% - 110px);
}

.action-bar {
  margin-top: 12px;
  text-align: right;
}

pre {
  background: #f6f8fa;
  padding: 12px;
  border-radius: 4px;
  overflow-x: auto;
}

code {
  font-family: monospace;
}

/* 等待效果 */
/* 加载动画样式 */
.loading-spinner {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 30px;
}

.spinner {
  width: 18px;
  height: 18px;
  border: 2px solid #f3f3f3;
  border-top: 2px solid #409eff;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
}

/* 调整消息气泡样式 */
.message-bubble.generating {
  min-height: 80px;
  padding: 20px;
}


.spinner {
  width: 28px;
  /* 调整大小 */
  height: 28px;
  border-width: 4px;
  /* 调整边框粗细 */
  border-top-color: #67C23A;
  /* 修改颜色 */
  animation-duration: 0.6s;
  /* 调整旋转速度 */
}


/* 样式优化 */
.leftDiv {
  width: 150px;
  margin: 3px 15px 0 0;
}

.iconBtn {
  color:#727272;
}

.message-text .think {
  background: #f5f5f5;
  color: #727272;
  font-size: 14px;
  padding: 20px;
  border-radius: 10px;
}

.stAudio{
  position:fixed;
  top:-20%;
  right:-300px;
  opacity: 0;
  z-index:-5;
}
.auto-audio-role img{
  width:50px;
  display: block;
  margin-bottom: 5px;
}
.auto-audio-role button{
  padding:0 0 5px;
  overflow: hidden;
}


.auto-audio-role.rensheng button{
  width:46%;margin:2%;
}
.auto-audio-role.rensheng button img{
  width:100%;
}


.icon-shouye{
  position:absolute;
  top:25px;
  right:25px;
  font-size: 25px;
  color:#fff;
  z-index: 5;
}


/* 右侧消失，左侧全屏 */
@media(max-width:1400px) { 
  .card-content-right{
    display: none;
  }
  .card-content-left{
    width:100%;
  }
}


@media(min-width:993px) {
  .input-bg {
    padding: 25px 15px;
  }
}

@media(max-width:992px) {
  /* 政府页面效果去除 */
  .card-header{
    display: none;
  }
  .card-content{
    margin-top:0;
  }
  .main-container1{
    padding:0;
  }
  .app-main,.card-content{
    height:100vh !important;
  }
  .card-content{
    border-radius: 0;
  }

  
  /* .input-div{margin:0;} */
  .message-container {
    padding: 50px 0 150px;
  }

  /* .Btn{height:30px;width:30px;display: block;} */
  .input-div {
    padding: 10px 15px 5px
  }

  /* 对话框 */
  .message-item {
    margin-bottom: 5px;
  }

  .message-bubble {
    max-width: calc(100vw - 45px);
  }

  .message-item.user .message-bubble {
    margin: 5px 5px 5px 0;
  }

  .message-item.system .message-bubble ,.message-item.assistant .message-bubble {
    margin: 5px 0 5px 5px;
  }

  .message-avatar {
    width: 30px;
    height: 30px;
    margin: 0 0 0 5px;
  }
  .icon-shouye{
    top:15px;
    right:10px;
    color:rgba(0, 0, 0, 0.7);
  }
}
</style>